/**
 * 
 */
package org.stars.daostars.core;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.stars.daostars.core.conditions.Condition;
import org.stars.daostars.sqlmapper.SqlMapper;
import org.stars.security.UserIdentity;

/**
 * Questa classe rappresenta una query. Oltre alla definizione della query
 * stessa, ritroviamo il nome della query, l'elenco dei parametri e se la query
 * è stata processata al fine di trovare dipendenze con altre query. Nel caso di
 * dipendenza, la query padre ed i relativi parametri vengono inseriti prima
 * della definizione sql e dei parametri della query attuale. La query eredita
 * anche il codice sql condizionale.
 * 
 * @author Francesco Benincasa (908099)
 * 
 */
public class SqlQuery implements Serializable {
	
	/**
	 * Mappa di conversione. Utilizzata sia per il dto che per gli oggetti in output.
	 */
	private String conversionMapName;


	/**
	 * @return the conversionMapName
	 */
	public String getConversionMapName() {
		return conversionMapName;
	}

	/**
	 * @param conversionMapName the conversionMapName to set
	 */
	public void setConversionMapName(String conversionMapName) {
		this.conversionMapName = conversionMapName;
	}

	/**
	 * Pre-condizione. Viene eseguita prima dell'invocazione di una query
	 */
	private String precondition;		
	
	/**
	 *	Serial id
	 */
	private static final long serialVersionUID = -4984093149643092102L;

	/**
	 * query di base. Il codice della query viene accodato alla query di base,
	 * se quest'ultima è presente.
	 */
	private String baseSqlQueryName;

	/**
	 * elenco degli statement sql che creano
	 */
	private List<SqlFragment> clauses;
	
	/**
	 * query incluse
	 */
	private List<String> usedQueryNameSet;

	/**
	 * definizione del dao
	 */
	protected SqlMapper definition;

	/**
	 * nome della query
	 */
	private String name;
	
	/**
	 * Nome della query che eventualmente viene posta in coda alla query
	 * come sezione order by
	 */
	private String orderByName;

	/**
	 * Codice sql nudo e crudo, senza elaborazioni.
	 */
	private String plainSql;

	/**
	 * Costruttore di default.
	 */
	public SqlQuery() {
		this("", null);
	}

	/**
	 * Costruttore.
	 * 
	 * @param name
	 *            nome della query
	 * @param daoDefinition
	 *            definizione del dao associato
	 */
	public SqlQuery(String name, SqlMapper daoDefinition) {
		this.name = name.toUpperCase();
		conversionMapName="";
		plainSql = "";
		clauses = new ArrayList<SqlFragment>();
		usedQueryNameSet=new ArrayList<String>();
		baseSqlQueryName = ""; // di default non ha query di base
		definition = daoDefinition;
		orderByName="";
		precondition="";
	}

	/**
	 * Restituisce l'elenco dei parametri COMPLETO di tutti i parametri
	 * utilizzati dalla query
	 * 
	 * @return <code>ArrayList</code> dei parametri della query
	 */
	public List<SqlParameter> getAllParameters() {
		ArrayList<SqlParameter> ret = new ArrayList<SqlParameter>();

		for (SqlFragment clause : clauses) {
			ret.addAll(clause.getAllParameters());
		}

		return ret;
	}

	/**
	 * Getter dell'attributo baseSqlQueryName.
	 * 
	 * @return the baseSqlQueryName
	 */
	public String getBaseSqlQueryName() {
		return baseSqlQueryName;
	}

	/**
	 * Getter dell'attributo clauses.
	 * 
	 * @return the clauses
	 */
	public List<SqlFragment> getClauses() {
		return clauses;
	}

	/**
	 * @return the definition
	 */
	public SqlMapper getDefinition() {
		return definition;
	}

	/**
	 * logger
	 * 
	 * @return logger
	 */
	public Log getLogger() {
		return LogFactory.getLog(SqlQuery.class);
	}

	/**
	 * Getter dell'attributo name.
	 * 
	 * @return the name
	 */
	public String getName() {
		return name;
	}

	/**
	 * @return the orderByName
	 */
	public String getOrderByName() {
		return orderByName;
	}

	/**
	 * Restituisce l'elenco dei parametri <i>non condizionati</i>, ovvero
	 * l'elenco dei parametri delle clause non condizionali. In caso di clausola
	 * condizionata, non restiusce alcun tipo di parametro
	 * 
	 * @return <code>ArrayList</code> dei parametri della query
	 */
	public List<SqlParameter> getParameters() {
		ArrayList<SqlParameter> ret = new ArrayList<SqlParameter>();

		for (SqlFragment clause : clauses) {
			if (!clause.isConditionedClause()) {
				ret.addAll(clause.getParameters());
			}
		}

		return ret;
	}

	/**
	 * A fronte dello user che la sta invocando e dei parametri passati alla
	 * query, otteniamo l'elenco dei parametri della query. Questo metodo
	 * analizza anche le parti condizionate della query.
	 * 
	 * @param user
	 *            utente che sta invocando la query
	 * @param eb
	 *            entity bean che contiene i parametri della query
	 * @param params
	 *            parametri passati alla query.
	 * @return elenco dei parametri
	 * @throws Exception
	 */
	public List<SqlParameter> getParameters(UserIdentity user,
			Object eb, Object[] params, Map<String, String> variables) throws Exception {
		ArrayList<SqlParameter> ret = new ArrayList<SqlParameter>();

		ArrayList<SqlParameter> sqlParams;

		// se non esiste l'array di indici lo creiamo
		if (params == null) {
			params = new Object[0];
		}

		for (SqlFragment clause : clauses) { 
			sqlParams = clause.getParameters(definition, user, eb, params, variables);

			if (sqlParams != null && sqlParams.size() > 0) {
				ret.addAll(sqlParams);
			}
		}

		return ret;
	}

	/**
	 * Getter dell'attributo plainSql.
	 * 
	 * @return the plainSql
	 */
	public String getPlainSql() {
		return plainSql;
	}

	/**
	 * Recupera l'sql del codice non condizionale che compone la query. L'sql
	 * ottenuto è il concatenamento delle query non condizionali. In questo caso
	 * non vengono risolte neanche le varibili.
	 * 
	 * @return codice sql in formato stringa
	 */
	public String getSql() {
		StringBuffer sb = new StringBuffer();

		for (SqlFragment clause : clauses) {
			if (!clause.isConditionedClause()) {
				sb.append(clause.getSql(null));
			}
		}

		return sb.toString();
	}
	
	/**
	 * Verifica che la precondizione per la query sia verificata. 
	 * 
	 * @param user
	 *			utente che sta invocando la query
	 * @param eb
	 *          parametri passati alla query
	 * @param params
	 *          parametri passati alla query
	 * @param variables
	 *          variabili definite per la singola istanza di dao
	 * @return
	 * 			restituisce <code>true</code> se la precondizione è rispettata.
	 * @throws Exception
	 * 			in caso di errore
	 */
	public boolean isPreconditionRespected(UserIdentity user, Object eb, Object[] params,
			Map<String, String> variables) throws Exception {
		boolean bRet=true;
		
		if (precondition.length()>0)
		{
			Condition condition=definition.getCondition(precondition);
			
			if (condition.isVerified(user, eb, params, variables))
			{
				bRet=true;
			} else bRet=false;
		}
		
		return bRet;
	}

	/**
	 * Recupera il codice sql in base ai parametri passati alla query.
	 * 
	 * @param user
	 *            utente che sta invocando la query
	 * @param eb
	 *            parametri passati alla query
	 * @param params
	 *            parametri passati alla query
	 * @param variables
	 *            variabili definite per la singola istanza di dao
	 * @return restituisce la query costruita in base ai parametri
	 * @throws Exception
	 */
	public String getSql(UserIdentity user, Object eb, Object[] params,
			Map<String, String> variables) throws Exception {
		StringBuffer sb = new StringBuffer();

		// se non esiste l'array di indici lo creiamo
		if (params == null) {
			params = new Object[0];
		}

		for (SqlFragment clause : clauses) {
			sb.append(clause.getSql(definition, user, eb, params, variables));
		}

		return sb.toString();
	}

	/**
	 * Setter dell'attributo baseSqlQueryName.
	 * 
	 * @param baseSqlQueryName
	 *            the baseSqlQueryName to set
	 */
	public void setBaseSqlQueryName(String baseSqlQueryName) {
		this.baseSqlQueryName = baseSqlQueryName;
	}

	/**
	 * Setter dell'attributo clauses.
	 * 
	 * @param clauses
	 *            the clauses to set
	 */
	public void setClauses(List<SqlFragment> clauses) {
		this.clauses = clauses;
	}

	/**
	 * @param definition
	 *            the definition to set
	 */
	public void setDefinition(SqlMapper definition) {
		this.definition = definition;
	}

	/**
	 * Setter dell'attributo name.
	 * 
	 * @param name
	 *            the name to set
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * @param orderByName the orderByName to set
	 */
	public void setOrderByName(String orderByName) {
		this.orderByName = orderByName;
	}

	/**
	 * Setter dell'attributo plainSql.
	 * 
	 * @param plainSql
	 *            the plainSql to set
	 */
	public void setPlainSql(String plainSql) {
		this.plainSql = plainSql;
	}

	/**
	 * @return the usedQueryNameSet
	 */
	public List<String> getUsedQueryNameSet() {
		return usedQueryNameSet;
	}

	/**
	 * @param usedQueryNameSet the usedQueryNameSet to set
	 */
	public void setUsedQueryNameSet(List<String> usedQueryNameSet) {
		this.usedQueryNameSet = usedQueryNameSet;
	}

	/**
	 * @return the precondition
	 */
	public String getPrecondition() {
		return precondition;
	}

	/**
	 * @param precondition the precondition to set
	 */
	public void setPrecondition(String precondition) {
		this.precondition = precondition;
	}

}
